/*
 *  ETA/OS - Python header
 *  Copyright (C) 2017   Michel Megens <dev@bietje.net>
 *  Copyright (C) 2017   Dean Hall
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __INTERP_H__
#define __INTERP_H__

/**
 * \file
 * \brief VM Interpreter
 *
 * VM interpreter header.
 */

#include <etaos/python/thread.h>

#define INTERP_LOOP_FOREVER          0
#define INTERP_RETURN_ON_NO_THREADS  1

/** Frame pointer ; currently for single thread */
#define PM_FP (gVmGlobal.pthread->pframe)
/** Instruction pointer */
#define PM_IP (PM_FP->fo_ip)
/** Argument stack pointer */
#define PM_SP (PM_FP->fo_sp)

/** top of stack */
#define TOS             (*(PM_SP - 1))
/** one under TOS */
#define TOS1            (*(PM_SP - 2))
/** two under TOS */
#define TOS2            (*(PM_SP - 3))
/** three under TOS */
#define TOS3            (*(PM_SP - 4))
/** index into stack; 0 is top, 1 is next */
#define STACK(n)        (*(PM_SP - ((n) + 1)))
/** pops an obj from the stack */
#define PM_POP()        (*(--PM_SP))
/** pushes an obj on the stack */
#define PM_PUSH(pobj)   (*(PM_SP++) = (pobj))
/** gets the argument (S16) from the instruction stream */
#define GET_ARG()       mem_getWord(PM_FP->fo_memspace, &PM_IP)

/** pushes an obj in the only stack slot of the native frame */
#define NATIVE_SET_TOS(pobj) (gVmGlobal.nativeframe.nf_stack = \
                        (pobj))
/** gets the nth local var from the native frame locals */
#define NATIVE_GET_LOCAL(n) (gVmGlobal.nativeframe.nf_locals[n])
/** gets a pointer to the frame that called this native fxn */
#define NATIVE_GET_PFRAME()   (*ppframe)
/** gets the number of args passed to the native fxn */
#define NATIVE_GET_NUM_ARGS() (gVmGlobal.nativeframe.nf_numlocals)

/**
 * COMPARE_OP enum.
 * Used by the COMPARE_OP bytecode to determine
 * which type of compare to perform.
 * Must match those defined in Python.
 */
typedef enum PmCompare_e {
	COMP_LT = 0,	    /**< less than */
	COMP_LE,	    /**< less than or equal */
	COMP_EQ,	    /**< equal */
	COMP_NE,	    /**< not equal */
	COMP_GT,	    /**< greater than */
	COMP_GE,	    /**< greater than or equal */
	COMP_IN,	    /**< is in */
	COMP_NOT_IN,	    /**< is not in */
	COMP_IS,	    /**< is */
	COMP_IS_NOT,	    /**< is not */
	COMP_EXN_MATCH	    /**< do exceptions match */
} PmCompare_t, *pPmCompare_t;

/**
 * Byte code enumeration
 */
typedef enum PmBcode_e {
	/*
	 * Python source to create this list:
	 * import dis
	 * o = dis.opname
	 * for i in range(256):
	 *     if o[i][0] != '<':
	 *         print "\t%s," % o[i]
	 *     else:
	 *         print "\tUNUSED_%02X," % i
	 */
	STOP_CODE = 0,		/* 0x00 */
	POP_TOP,
	ROT_TWO,
	ROT_THREE,
	DUP_TOP,
	ROT_FOUR,
	UNUSED_06,
	UNUSED_07,
	UNUSED_08,
	NOP,
	UNARY_POSITIVE,		/* d010 */
	UNARY_NEGATIVE,
	UNARY_NOT,
	UNARY_CONVERT,
	UNUSED_0E,
	UNARY_INVERT,
	UNUSED_10,		/* 0x10 */
	UNUSED_11,
	LIST_APPEND,
	BINARY_POWER,
	BINARY_MULTIPLY,	/* d020 */
	BINARY_DIVIDE,
	BINARY_MODULO,
	BINARY_ADD,
	BINARY_SUBTRACT,
	BINARY_SUBSCR,
	BINARY_FLOOR_DIVIDE,
	BINARY_TRUE_DIVIDE,
	INPLACE_FLOOR_DIVIDE,
	INPLACE_TRUE_DIVIDE,
	SLICE_0,		/* d030 */
	SLICE_1,
	SLICE_2,		/* 0x20 */
	SLICE_3,
	UNUSED_22,
	UNUSED_23,
	UNUSED_24,
	UNUSED_25,
	UNUSED_26,
	UNUSED_27,
	STORE_SLICE_0,		/* d040 */
	STORE_SLICE_1,
	STORE_SLICE_2,
	STORE_SLICE_3,
	UNUSED_2C,
	UNUSED_2D,
	UNUSED_2E,
	UNUSED_2F,
	UNUSED_30,		/* 0x30 */
	UNUSED_31,
	DELETE_SLICE_0,		/* d050 */
	DELETE_SLICE_1,
	DELETE_SLICE_2,
	DELETE_SLICE_3,
	STORE_MAP,
	INPLACE_ADD,
	INPLACE_SUBTRACT,
	INPLACE_MULTIPLY,
	INPLACE_DIVIDE,
	INPLACE_MODULO,
	STORE_SUBSCR,		/* d060 */
	DELETE_SUBSCR,
	BINARY_LSHIFT,
	BINARY_RSHIFT,
	BINARY_AND,		/* 0x40 */
	BINARY_XOR,
	BINARY_OR,
	INPLACE_POWER,
	GET_ITER,
	UNUSED_45,
	PRINT_EXPR,		/* d070 */
	PRINT_ITEM,
	PRINT_NEWLINE,
	PRINT_ITEM_TO,
	PRINT_NEWLINE_TO,
	INPLACE_LSHIFT,
	INPLACE_RSHIFT,
	INPLACE_AND,
	INPLACE_XOR,
	INPLACE_OR,
	BREAK_LOOP,		/* 0x50 *//* d080 */
	WITH_CLEANUP,
	LOAD_LOCALS,
	RETURN_VALUE,
	IMPORT_STAR,
	EXEC_STMT,
	YIELD_VALUE,
	POP_BLOCK,
	END_FINALLY,
	BUILD_CLASS,

	/* Opcodes from here have an argument */
	HAVE_ARGUMENT = 90,	/* d090 */
	STORE_NAME = 90,
	DELETE_NAME,
	UNPACK_SEQUENCE,
	FOR_ITER,
	UNUSED_5E,
	STORE_ATTR,
	DELETE_ATTR,		/* 0x60 */
	STORE_GLOBAL,
	DELETE_GLOBAL,
	DUP_TOPX,
	LOAD_CONST,		/* d100 */
	LOAD_NAME,
	BUILD_TUPLE,
	BUILD_LIST,
	BUILD_MAP,
	LOAD_ATTR,
	COMPARE_OP,
	IMPORT_NAME,
	IMPORT_FROM,
	UNUSED_6D,
	JUMP_FORWARD,		/* d110 */
	JUMP_IF_FALSE,
	JUMP_IF_TRUE,		/* 0x70 */
	JUMP_ABSOLUTE,
	UNUSED_72,
	UNUSED_73,
	LOAD_GLOBAL,
	UNUSED_75,
	UNUSED_76,
	CONTINUE_LOOP,
	SETUP_LOOP,		/* d120 */
	SETUP_EXCEPT,
	SETUP_FINALLY,
	UNUSED_7B,
	LOAD_FAST,
	STORE_FAST,
	DELETE_FAST,
	UNUSED_79,
	UNUSED_80,		/* 0x80 */
	UNUSED_81,
	RAISE_VARARGS,		/* d130 */
	CALL_FUNCTION,
	MAKE_FUNCTION,
	BUILD_SLICE,
	MAKE_CLOSURE,
	LOAD_CLOSURE,
	LOAD_DEREF,
	STORE_DEREF,
	UNUSED_8A,
	UNUSED_8B,
	CALL_FUNCTION_VAR,	/* d140 */
	CALL_FUNCTION_KW,
	CALL_FUNCTION_VAR_KW,
	EXTENDED_ARG,

	UNUSED_90, UNUSED_91, UNUSED_92, UNUSED_93,
	UNUSED_94, UNUSED_95, UNUSED_96, UNUSED_97,
	UNUSED_98, UNUSED_99, UNUSED_9A, UNUSED_9B,
	UNUSED_9C, UNUSED_9D, UNUSED_9E, UNUSED_9F,
	UNUSED_A0, UNUSED_A1, UNUSED_A2, UNUSED_A3,
	UNUSED_A4, UNUSED_A5, UNUSED_A6, UNUSED_A7,
	UNUSED_A8, UNUSED_A9, UNUSED_AA, UNUSED_AB,
	UNUSED_AC, UNUSED_AD, UNUSED_AE, UNUSED_AF,
	UNUSED_B0, UNUSED_B1, UNUSED_B2, UNUSED_B3,
	UNUSED_B4, UNUSED_B5, UNUSED_B6, UNUSED_B7,
	UNUSED_B8, UNUSED_B9, UNUSED_BA, UNUSED_BB,
	UNUSED_BC, UNUSED_BD, UNUSED_BE, UNUSED_BF,
	UNUSED_C0, UNUSED_C1, UNUSED_C2, UNUSED_C3,
	UNUSED_C4, UNUSED_C5, UNUSED_C6, UNUSED_C7,
	UNUSED_C8, UNUSED_C9, UNUSED_CA, UNUSED_CB,
	UNUSED_CC, UNUSED_CD, UNUSED_CE, UNUSED_CF,
	UNUSED_D0, UNUSED_D1, UNUSED_D2, UNUSED_D3,
	UNUSED_D4, UNUSED_D5, UNUSED_D6, UNUSED_D7,
	UNUSED_D8, UNUSED_D9, UNUSED_DA, UNUSED_DB,
	UNUSED_DC, UNUSED_DD, UNUSED_DE, UNUSED_DF,
	UNUSED_E0, UNUSED_E1, UNUSED_E2, UNUSED_E3,
	UNUSED_E4, UNUSED_E5, UNUSED_E6, UNUSED_E7,
	UNUSED_E8, UNUSED_E9, UNUSED_EA, UNUSED_EB,
	UNUSED_EC, UNUSED_ED, UNUSED_EE, UNUSED_EF,
	UNUSED_F0, UNUSED_F1, UNUSED_F2, UNUSED_F3,
	UNUSED_F4, UNUSED_F5, UNUSED_F6, UNUSED_F7,
	UNUSED_F8, UNUSED_F9, UNUSED_FA, UNUSED_FB,
	UNUSED_FC, UNUSED_FD, UNUSED_FE, UNUSED_FF
} PmBcode_t, *pPmBcode_t;

/**
 * Interprets the available threads. Does not return.
 *
 * @param returnOnNoThreads Loop forever if 0, exit with status if no more
 *                          threads left.
 * @return Return status if called with returnOnNoThreads != 0,
 *         will not return otherwise.
 */
PmReturn_t interpret(const uint8_t returnOnNoThreads);

/**
 * Selects a thread to run and changes the VM internal variables to
 * let the switch-loop execute the chosen one in the next iteration.
 * For the moment the algorithm is primitive and will change the
 * thread each time it is called in a round-robin fashion.
 */
PmReturn_t interp_reschedule(void);

/**
 * Creates a thread object and adds it to the queue of threads to be
 * executed while interpret() is running.
 *
 * The given obj may be a function, module, or class.
 * Creates a frame for the given function.
 *
 * @param pfunc Ptr to function to be executed as a thread.
 * @return Return status
 */
PmReturn_t interp_addThread(pPmFunc_t pfunc);

/**
 * Sets the  reschedule flag.
 *
 * @param boolean Reschedule on next occasion if boolean is true; clear
 *                the flag otherwise.
 */
void interp_setRescheduleFlag(uint8_t boolean);

#endif				/* __INTERP_H__ */
